#include "ConvertCgns.h"

#ifdef _WIN32
#include <direct.h>
#include <io.h>
#else
#include <unistd.h>
#endif
#include <iostream>
#include <map>
#include <vector>

#include <vtkCGNSReader.h>
#include <vtkMultiBlockDataSet.h>
#include <vtkInformation.h>
#include <vtkDataObject.h>
#include <vtkUnstructuredGrid.h>
#include <vtkStructuredGrid.h>
#include <vtkPolyData.h>
#include <vtkDataSet.h>
#include <vtkAppendFilter.h>
#include <vtkFieldData.h>

#include <Python.h>

#include "IBE_DataInformationToVTK.h"
#include "IBE_Common.h"

static std::map<std::string, std::map<std::string, std::vector<std::string>>> seriesFilePathsMap_;

// ----------------------------------------------------------------------------
vtkUnstructuredGrid* ConvertStructuredToUnstructuredGrid( vtkDataObject* dataObj )
{
    if( !dataObj || !( dataObj->IsA( "vtkStructuredGrid" ) || dataObj->IsA( "vtkPolyData" ) ) )
    {
        return nullptr;
    }

    vtkAppendFilter* appendFilter = vtkAppendFilter::New();
    appendFilter->AddInputData( 0, dataObj );
    appendFilter->Update();

    auto appendData = appendFilter->GetOutput();
    appendData->Register( appendData );
    appendFilter->Delete();

    return appendData;
}

// ----------------------------------------------------------------------------
void ConvertDataToUnstructuredGrid( vtkMultiBlockDataSet* multiData )
{
    if( !multiData || !multiData->IsA( "vtkMultiBlockDataSet" ) )
    {
        return;
    }

    auto numOfBlocks = multiData->GetNumberOfBlocks();
    for( int i = 0; i < numOfBlocks; i++ )
    {
        auto blockData = multiData->GetBlock( i );
        if( blockData->IsA( "vtkMultiBlockDataSet" ) )
        {
            vtkMultiBlockDataSet* mData = vtkMultiBlockDataSet::SafeDownCast( blockData );
            ConvertDataToUnstructuredGrid( mData );
        }
        else if( blockData->IsA( "vtkStructuredGrid" )  || blockData->IsA( "vtkPolyData" ) )
        {
            vtkUnstructuredGrid* unStructData = ConvertStructuredToUnstructuredGrid( blockData );
            multiData->SetBlock( i, unStructData );
            unStructData->Delete();
        }
    }
}

// ----------------------------------------------------------------------------
void RemoveAllFieldData( vtkDataObject* dataObj )
{
    if( !dataObj )
    {
        return;
    }

    if( dataObj->IsA("vtkMultiBlockDataSet") )
    {
        vtkMultiBlockDataSet* multiData = vtkMultiBlockDataSet::SafeDownCast( dataObj );
        auto numOfBlocks = multiData->GetNumberOfBlocks();
        for( int i = 0; i < numOfBlocks; i++ )
        {
            auto blockData = multiData->GetBlock( i );
            RemoveAllFieldData( blockData );
        }
    }
    else if( dataObj->IsA( "vtkDataSet" ) )
    {
        vtkDataSet* mData = vtkDataSet::SafeDownCast( dataObj );
        auto fieldData = dataObj->GetFieldData();
        int numOfArrays = fieldData->GetNumberOfArrays();
        for( int i = 0; i < numOfArrays; i++ )
        {
            fieldData->RemoveArray( 0 );
        }
    }
}

// ----------------------------------------------------------------------------
ConvertCgns::ConvertCgns()
{
}

// ----------------------------------------------------------------------------
ConvertCgns::~ConvertCgns()
{
}

// ----------------------------------------------------------------------------
int ConvertCgns::ConvertCgnsToFile(std::string inputFile, std::string outputDir, int outType)
{
    if (outType != 0 && outType != 1)
    {
        return 0;
    }

    // 去除前后空格
    inputFile = ibe_common::IBE_Common::StringTrimmed(inputFile.c_str());
    outputDir = ibe_common::IBE_Common::StringTrimmed(outputDir.c_str());
    if (inputFile.empty() || outputDir.empty())
    {
        return 0;
    }

    seriesFilePathsMap_.clear();

    // 获取输入文件名
    std::string inFileName = inputFile;
    auto slashPos = inputFile.find_last_of("/");
    if (slashPos != std::string::npos)
    {
        inFileName = inputFile.substr(slashPos + 1);
    }

    std::string outFolderDir_vtk = "";
    std::string outFolderDir_gmsh = "";
    {
        auto dot_pos = inFileName.find_last_of('.');
        std::string fileName = inFileName;
        if (dot_pos != std::string::npos)
        {
            fileName = inFileName.substr(0, dot_pos);
        }
        outFolderDir_vtk = outputDir + "/" + fileName + ".vtk";
        outFolderDir_gmsh = outputDir + "/" + fileName + ".gmsh";
        // 判断文件是否存在:0-存在，-1-不存在 
        if (access(outFolderDir_vtk.c_str(), 0) == -1)
        {
            // 创建文件夹
#ifdef _WIN32
            mkdir(outFolderDir_vtk.c_str());
#else
            mkdir(outFolderDir_vtk.c_str(), 0755);
#endif
        }
        // 判断文件是否存在:0-存在，-1-不存在 
        if (access(outFolderDir_gmsh.c_str(), 0) == -1)
        {
            // 创建文件夹
#ifdef _WIN32
            mkdir(outFolderDir_gmsh.c_str());
#else
            mkdir(outFolderDir_gmsh.c_str(), 0755);
#endif
        }
    }

    int conVtkResult = ConvertCgns::ConvertCgnsToVTK(inputFile, outFolderDir_vtk);
    if (outType == 1) // to gmsh
    {
        if (conVtkResult == 1 && !seriesFilePathsMap_.empty())
        {
            conVtkResult = ConvertCgns::ConvertCgnsToGmsh(inputFile, outFolderDir_gmsh);
        }
        else
        {
            conVtkResult = 0;
        }
    }

    return conVtkResult;
}

// ----------------------------------------------------------------------------
int ConvertCgns::ConvertCgnsToVTK(std::string inputFile, std::string outputDir)
{
    int convertState = 0;
    try
    {
        std::cout << "Start convert \"" << inputFile << "\" to \"" << outputDir << "\"." << std::endl;

        vtkCGNSReader* reader = vtkCGNSReader::New();
        reader->SetFileName(inputFile.c_str());
        reader->Update();

        auto inputData = reader->GetOutput();
        if (inputData)
        {
            std::cout << "[CONVERSION PROCESS]50%" << std::endl;

            ConvertDataToUnstructuredGrid( inputData );
            RemoveAllFieldData( inputData );

            vtkMultiBlockDataSet* multiData = vtkMultiBlockDataSet::New();
            multiData->SetNumberOfBlocks(1);
            multiData->SetBlock(0, inputData);
            vtkInformation* caseMetaData = multiData->GetMetaData(static_cast<unsigned int>(0));
            caseMetaData->Set(vtkCompositeDataSet::NAME(), "0"); // 设置分析步名称

            std::cout << "[CONVERSION PROCESS]60%" << std::endl;

            ibe_vtk::IBE_DataInformationToVTK* dataToVtk = new ibe_vtk::IBE_DataInformationToVTK();
            seriesFilePathsMap_ = dataToVtk->WriteDataToVtk(multiData, outputDir.c_str());

            multiData->Delete();
            multiData = nullptr;
            delete dataToVtk;
            dataToVtk = nullptr;

            std::cout << "[CONVERSION PROCESS]100%" << std::endl;
            convertState = 1;
        }
        reader->Delete();
    }
    catch (...)
    {
        /*std::cerr << "Convert fialed." << std::endl;
        return 0;*/
    }

    return convertState;
}

// ----------------------------------------------------------------------------
int ConvertCgns::ConvertCgnsToGmsh(std::string inputFile, std::string outputDir)
{
    Py_Initialize(); // 只调用一次
    if (!Py_IsInitialized()) // 判断是否初始化成功
    {
        std::cout << "Python Initialize failed." << std::endl;
        return 0;
    }

    int convertState = 0;
    std::vector<PyObject*> pyObjects;

    try
    {
        std::cout << "Start convert \"" << inputFile << "\" to \"" << outputDir << "\"." << std::endl;

        //导入模块 
        PyObject* module = PyImport_ImportModule("ConvertVtkToGmsh_CGNS");
        if (!module)
        {
            std::logic_error ex("Python get module(ConvertVtkToGmsh_CGNS) failed.");
            throw std::exception(ex);
        }
        pyObjects.emplace_back(module);

        std::cout << "[CONVERSION PROCESS] 25%" << std::endl;

        // vtk转gmsh的转换接口
        PyObject* func = PyObject_GetAttrString(module, "MultiVtkToGmsh");
        if (!func || !PyCallable_Check(func))
        {
            std::logic_error ex("Can't find the function(MultiVtkToGmsh).");
            throw std::exception(ex);
        }
        pyObjects.emplace_back(func);

        std::cout << "[CONVERSION PROCESS] 50%" << std::endl;

        // 构造参数
        std::vector<std::string> seriesFilePaths;
        for (auto& filePathsMapIter : seriesFilePathsMap_)
        {
            for (auto& filePathsIter : filePathsMapIter.second)
            {
                auto& filePaths = filePathsIter.second;
                for (auto& filePath : filePaths)
                {
                    seriesFilePaths.emplace_back(filePath);
                }
            }
        }
        PyObject* filePathList_py = PyList_New(seriesFilePaths.size());
        for (auto i = 0; i < seriesFilePaths.size(); i++)
        {
            std::string& filePath = seriesFilePaths.at(i);
            PyList_SetItem(filePathList_py, i, PyUnicode_FromString(filePath.c_str()));
        }
        pyObjects.emplace_back(filePathList_py);

        PyObject* func_args = PyTuple_New(2);
        PyTuple_SetItem(func_args, 0, filePathList_py);
        PyTuple_SetItem(func_args, 1, PyUnicode_FromString(outputDir.c_str()));
        pyObjects.emplace_back(func_args);

        std::cout << "[CONVERSION PROCESS] 75%" << std::endl;

        // 调用转换接口
        PyObject* func_py = PyObject_CallObject(func, func_args);
        if (!func_py)
        {
            std::logic_error ex("MultiVtkToGmsh failed.");
            throw std::exception(ex);
        }
        pyObjects.emplace_back(func_py);

        std::cout << "[CONVERSION PROCESS] 100%" << std::endl;
        convertState = 1;
    }
    catch (const std::exception& ex)
    {
        convertState = 0;
        std::cerr << "[ERROR] ConvertCgnsToGmsh: " << ex.what() << std::endl;
        if (PyErr_Occurred())
        {
            PyErr_Print();
        }
    }

    // 释放python空间
    // Py_XDECREF和Py_DECREF功能相同，但可以处理空指针
    for (auto it = pyObjects.rbegin(); it != pyObjects.rend(); it++)
    {
        Py_XDECREF(*it); // 释放python空间
    }
    std::vector<PyObject*>().swap(pyObjects);

    Py_Finalize(); // 只调用一次

    return convertState;
}
